package com.wendy.utils;

import com.google.common.hash.Hashing;
import lombok.extern.slf4j.Slf4j;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;

/**
 * @author zhuwending
 */
@Slf4j
public class AlgorithmUtil {

    public static byte[] encryptDES2Byte(String content, String key) {
        byte[] bytes = null;
        try {
            Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
            IvParameterSpec iv = new IvParameterSpec(key.getBytes(StandardCharsets.UTF_8));
            cipher.init(1, loadDesKey(key), iv);
            bytes = cipher.doFinal(content.getBytes(StandardCharsets.UTF_8));
        } catch (Exception e) {
            log.error("encrypt failed", e);
        }
        return bytes;
    }

    public static byte[] decryptDES2Byte(String content, String key) {
        byte[] bytes = null;
        try {
            IvParameterSpec ivParameterSpec = new IvParameterSpec(key.getBytes(StandardCharsets.UTF_8));
            byte[] source = hex2byte(content);
            Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
            cipher.init(2, loadDesKey(key), ivParameterSpec);
            bytes = cipher.doFinal(source);
        } catch (Exception e) {
            log.error("decrypt failed", e);
        }
        return bytes;
    }

    public static String encryptDES2String(String content, String key) {
        byte[] bytes = null;
        try {
            Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
            IvParameterSpec ivParameterSpec = new IvParameterSpec(key.getBytes(StandardCharsets.UTF_8));
            cipher.init(1, loadDesKey(key), ivParameterSpec);
            bytes = cipher.doFinal(content.getBytes(StandardCharsets.UTF_8));
        } catch (Exception e) {
            log.error("error", e);
        }
        return bytes2hex(bytes);
    }

    public static String decryptDES2String(String content, String key) {
        byte[] bytes = null;
        try {
            IvParameterSpec ivParameterSpec = new IvParameterSpec(key.getBytes(StandardCharsets.UTF_8));
            byte[] byteArray = hex2byte(content);
            Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
            cipher.init(2, loadDesKey(key), ivParameterSpec);
            bytes = cipher.doFinal(byteArray);
        } catch (Exception e) {
            log.error("error", e);
        }
        return bytes == null ? "" : new String(bytes);
    }

    public static byte[] encryptAES2Byte(String source, String key) {
        byte[] bytes = null;
        try {
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(1, loadKeyAes(key.getBytes(StandardCharsets.UTF_8)), new IvParameterSpec(key.getBytes(StandardCharsets.UTF_8)));
            bytes = cipher.doFinal(source.getBytes(StandardCharsets.UTF_8));
        } catch (Exception e) {
            log.error("error", e);
        }
        return bytes;
    }

    public static byte[] decryptAES2Byte(String source, String key) {
        byte[] bytes = null;
        try {
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(2, loadKeyAes(key.getBytes(StandardCharsets.UTF_8)), new IvParameterSpec(key.getBytes(StandardCharsets.UTF_8)));
            bytes = cipher.doFinal(hex2byte(source));
        } catch (Exception e) {
            log.error("error", e);
        }
        return bytes;
    }

    public static String encryptAES2String(String source, String key) {
        byte[] bytes = null;
        try {
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(1, loadKeyAes(key.getBytes(StandardCharsets.UTF_8)), new IvParameterSpec(key.getBytes(StandardCharsets.UTF_8)));
            bytes = cipher.doFinal(source.getBytes(StandardCharsets.UTF_8));
        } catch (Exception e) {
            log.error("error", e);
        }
        return bytes2hex(bytes);
    }

    public static String decryptAES2String(String source, String key) {
        byte[] bytes = null;
        try {
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(1, loadKeyAes(key.getBytes(StandardCharsets.UTF_8)), new IvParameterSpec(key.getBytes(StandardCharsets.UTF_8)));
            bytes = cipher.doFinal(hex2byte(source));
        } catch (Exception e) {
            log.error("error", e);
        }
        return bytes == null ? "" : new String(bytes);
    }

    public static String bytes2hex(byte[] bytes) {
        if (null == bytes || bytes.length == 0) {
            return "";
        } else {
            StringBuilder sb = new StringBuilder();
            for (byte b : bytes) {
                String temp = Integer.toHexString(b & 255);
                if (temp.length() == 1) {
                    sb.append("0");
                }
                sb.append(temp);
            }
            return sb.toString();
        }
    }

    public static String md5(String s) {
        String salt = "wendy";
        return Hashing.md5().hashString(s + salt, StandardCharsets.UTF_8).toString();
    }

    public static String sha256(String s) {
        return Hashing.sha256().hashString(s, StandardCharsets.UTF_8).toString();
    }

    public static String sha512(String s) {
        return Hashing.sha512().hashString(s, StandardCharsets.UTF_8).toString();
    }

    public static byte[] hex2byte(String hex) {
        byte[] digest = new byte[hex.length() / 2];
        for (int i = 0; i < digest.length; ++i) {
            String byteString = hex.substring(2 * i, 2 * i + 2);
            int byteValue = Integer.parseInt(byteString, 16);
            digest[i] = (byte) byteValue;
        }
        return digest;
    }

    private static SecretKey loadDesKey(String key) {
        SecretKey secretKey = null;
        try {
            byte[] bytes = key.getBytes(StandardCharsets.UTF_8);
            DESKeySpec desKeySpec = new DESKeySpec(bytes);
            SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("DES");
            secretKey = secretKeyFactory.generateSecret(desKeySpec);
        } catch (Exception e) {
            log.error("load secretKey failed", e);
        }
        return secretKey;
    }

    private static SecretKey loadKeyAes(byte[] bytes) {
        return new SecretKeySpec(bytes, "AES");
    }
}
